// npx babel-node object_demo01.js

let test01 = function() {
    const foo = 'bar'
    const baz = {foo}
    console.log(baz)

    // 等同于
    const baz1 = {foo: foo}
    console.log(baz1)
}

// test01()

function f1(x, y) {
    return {x, y};
}

// 等同于
function f2(x, y) {
    return {x: x, y: y};
}

let test02 = function(){
    let _
    _ = f1(1, 2)
    console.log(_)
    _ = f2(1, 2)
    console.log(_)
}

// test02()

// 除了属性简写，方法也可以简写。

let test03 = function() {
    const o1 = {
        method() {
            return "Hello!";
        }
    }

    // 等同于
    const o2 = {
        method: function() {
            return "Hello!";
        }
    }

    let _ = o1.method()
    console.log(_)
    _ = o2.method()
    console.log(_)
}

// test03()

let test04 = function(){
    let birth = '2000/01/01'
    const Person = {
        name: '张三',
        birth,
        hello() {
            console.log('我的名字是: ', this.name, ',我的生日是: ', this.birth)
        }
    }
    Person.hello()
}

// test04()

// 这种写法用于函数的返回值，将会非常方便。

function getPoint() {
    const x = 1
    const y = 10
    return {x, y}
}

let test05 = function() {
    let _ = getPoint()
    console.log(_)
}

// test05()

// CommonJS 模块输出一组变量，就非常合适使用简洁写法。

let test06 = function() {
    const ms = require('./object_module_demo02')
    ms.setItem("1", "9527")
    ms.setItem("zhangsan", "Hello, World!")
    ms.setItem("张三", "我爱北京天安门")
    console.log(ms.getItem("1"))
    console.log(ms.getItem("zhangsan"))
    console.log(ms.getItem("张三"))
}

// test06()

// 属性的赋值器（setter）和取值器（getter），事实上也是采用这种写法。

let test07 = function() {
    const cart = {
        _wheels: 4,
    
        get wheels () {
            return this._wheels
        },
    
        set wheels (value) {
            if (value < this._wheels) {
                throw new Error('数值太小了！')
            }
            this._wheels = value
        }
    }

    console.log(cart.wheels)
    cart.wheels = 5
    console.log(cart.wheels)
    cart.wheels = 3
}

// test07()

// 简洁写法在打印对象时也很有用。

let test08 = function() {
    let user = {
        name: 'test'
    }
    let foo = {
        bar: 'baz'
    }
    console.log(user, foo)
    console.log({user, foo})
}

// test08()

// ES6 允许字面量定义对象时，用表达式作为对象的属性名

let test09 = function() {
    let propKey = 'foo'

    let obj = {
        [propKey]: true,
        ['a' + 'bc']: 123
    }

    console.log(obj)
}

// test09()

let test10 = function() {
    let lastWord = 'last word'

    const a = {
        'first world': 'hello',
        [lastWord]: 'word'
    }

    console.log(a['first world'])
    console.log(a[lastWord])
    console.log(a['last word'])
}

// test10()

// 表达式还可以用于定义方法名。

let test11 = function() {
    let obj = {
        ['h' + 'ello']() {
            return 'hi'
        }
    }

    console.log(obj.hello())
}

// test11()

// 方法的 name 属性

let test12 = function() {
    const person = {
        name: 'wxg',
        sayName() {
            console.log('Hello', this.name)
        },
    }

    person.sayName()
    console.log(person.sayName.name)
}

// test12()

// 取值函数（getter）和存值函数（setter）  |  name

let test13 = function() {
    const obj = {
        get foo() {},
        set foo(x) {}
    }

    let _ 

    // 报错
    // TypeError: Cannot read property 'name' of undefined
    // _ = obj.foo.name
    // console.log(_)

    const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo')

    _ = descriptor.get.name
    console.log(_)
    _ = descriptor.set.name
    console.log(_)
}

// test13()

// 有两种特殊情况：bind方法创造的函数，name属性返回bound加上原函数的名字；Function构造函数创造的函数，name属性返回anonymous。

let test14 = function() {
    let _

    _ = (new Function()).name
    console.log(_)

    let doSomething = function() {
        //...
    }
    _ = doSomething.bind().name
    console.log(_)
}

// test14()

// 如果对象的方法是一个 Symbol 值，那么name属性返回的是这个 Symbol 值的描述。

let test15 = function() {
    const key1 = Symbol('description')
    const key2 = Symbol()

    let obj = {
        [key1]() {},
        [key2]() {},
    }

    let _
    _ = obj[key1].name
    console.log(_)

    _ = obj[key2].name
    console.log(_)
}

// 这里的预期和文档上的描述不符
// test15()

// 对象的每个属性都有一个描述对象（Descriptor），用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

let test16 = function() {
    let obj = { foo: 123 }
    let _ = Object.getOwnPropertyDescriptor(obj, 'foo')
    console.log(_)
}

// test16()

// 比如，对象原型的toString方法，以及数组的length属性，就通过“可枚举性”，从而避免被for...in遍历到。

let test17 = function() {
    let _ 
    _ = Object.getOwnPropertyDescriptor(Object.prototype, 'toString')
    console.log(_)

    _ = Object.getOwnPropertyDescriptor([], 'length').enumerable
    console.log(_)
}

// test17()

// 另外，ES6 规定，所有 Class 的原型的方法都是不可枚举的。

let test18 = function() {
    let _
    _ = Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
    console.log(_)
}

// test18()

// 总的来说，操作中引入继承的属性会让问题复杂化，大多数时候，我们只关心对象自身的属性。所以，尽量不要用for...in循环，而用Object.keys()代替。

// ---

let test19 = function() {
    let _
    _ = Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
    console.log(_)
}

// test19()

// super 关键字

let test20 = function() {
    const proto = {
        foo: 'hello'
    }

    const obj = {
        foo: 'world',
        find() {
            return super.foo;
        }
    }

    Object.setPrototypeOf(obj, proto)
    let _
    _ = obj.find()
    console.log(_)
}

// test20()

// 方法执行原型，绑定的this还是当前对象

let test21 = function() {
    const proto = {
        x: 'hello',
        foo() {
            console.log(this.x)
        }
    }

    const obj = {
        x: 'world',
        foo() {
            super.foo()
        }
    }

    Object.setPrototypeOf(obj, proto)

    obj.foo()
}

// test21()

// 对象的扩展运算符

let test22 = function() {
    // 解构赋值
    let {x, y, ...z} = { x:1, y:2, a:3, b:4 }

    console.log({x, y, z})
}

// test22()

// 解构赋值的拷贝是浅拷贝

let test23 = function() {
    let obj = { a: { b: 1} }
    let {...x} = obj
    obj.a.b = 2
    console.log(x.a.b)
}

// test23()

// 扩展运算符的解构赋值，不能复制继承自原型对象的属性

let test24 = function() {
    let o1 = { a:1 }
    let o2 = { b:2 }
    o2.__proto__ = o1
    let {...o3} = o2
    console.log(o3)
    console.log(o3.a)
}

// test24()

let test25 = function() {
    const o = Object.create({ x:1, y: 2 })
    o.z = 3

    let { x, ...newObj } = o
    let { y, z } = newObj

    console.log(x, y, z)
}

// test25()

// 解构赋值的一个用处，是扩展某个函数的参数，引入其他操作。

function baseFunction({ a, b }) {
    // ...
}
function wrapperFunction({ x, y, ...restConfig}) {
    // 使用 x 和 y 参数进行操作
    // 其余参数传给原始函数
    return baseFunction(restConfig)
}

// 扩展运算符 ...

let test26 = function() {
    let z = { a: 3, b: 4 }
    let n = { ...z }
    console.log(n)

    // 由于数组是特殊的对象，所以对象的扩展运算符也可以用于数组。
    let foo = { ...['a', 'b', 'c'] }
    console.log(foo)

    let _

    _ = {...1}
    console.log(_)

    _ = {...true}
    console.log(_)

    _ = {...undefined}
    console.log(_)

    _ = {...null}
    console.log(_)

    _ = {...'hello'}
    console.log(_)
}

// test26()

// 如果想完整克隆一个对象，还拷贝对象原型的属性

let test27 = function() {
    let obj = {}
    // 写法一
    const clone1 = {
        __proto__: Object.getPrototypeOf(obj),
        ...obj
    }

    // 写法二
    const clone2 = Object.assign(
        Object.create(Object.getPrototypeOf(obj)),
        obj
    )

    // 写法三
    const clone3 = Object.create(
        Object.getPrototypeOf(obj),
        Object.getOwnPropertyDescriptors(obj)
    )

}

// test27()

// 扩展运算符可以用于合并两个对象。

let test28 = function() {
    let a = { a: 1, b: 2 }
    let b = { c: 3 }

    let ab = { ...a, ...b }
    // 等同于
    let ab1 = Object.assign({}, a, b)

    console.log(ab)
    console.log(ab1)
}

// test28()

//  ES2020 引入了“链判断运算符”（optional chaining operator）?.



































